﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace ObjectPool
{
    /// <summary>
    /// 该对象池中的对象为可重复使用的对象，其中管理的对象会一直存在直到调用 <see cref="ReleaseAll"/> 释放所有对象
    /// </summary>
    /// <typeparam name="T">待管理的对象，必须实现 <see cref="IManaged"/> 接口 </typeparam>
    public class ReuseObjectPool<T> : IObjectPool<T> where T : IManaged
    {
        private Queue<T> _storedObjects;        // 存储对象的容器
        private List<T> _extractedObjects;      // 取出对象的容器


        public ReuseObjectPool(int capacity)
        {
            Capacity = capacity;
            _storedObjects = new Queue<T>(capacity);
            _extractedObjects = new List<T>();
        }

        public int Count => _storedObjects.Count;

        public int Capacity
        {
            get;
            private set;
        }

        public bool Store(T managedObject)
        {
            if (managedObject == null)
            {
                return false;
            }
            if (_storedObjects.Contains<T>(managedObject))
            {
                return false;
            }
            if (_extractedObjects.Contains<T>(managedObject))
            {
                return false;
            }
            managedObject.Init();
            (managedObject as IManagedEvent)?.OnStore();

            _storedObjects.Enqueue(managedObject);
            return true;
        }

        public T Extract()
        {
            T obj = _storedObjects.Dequeue();
            if (obj == null)
            {
                return obj;
            }
            (obj as IManagedEvent)?.OnExtract();

            _extractedObjects.Add(obj);
            return obj;
        }


        public bool Restore(T managedObject)
        {
            if (managedObject == null)
            {
                return false;
            }
            if (_storedObjects.Contains<T>(managedObject))
            {
                return false;
            }
            if (!_extractedObjects.Contains<T>(managedObject))
            {
                return false;
            }
            (managedObject as IManagedEvent)?.OnRestore();

            managedObject.Clear();
            _extractedObjects.Remove(managedObject);
            _storedObjects.Enqueue(managedObject);
            return true;
        }



        public void Release(T managedObject)
        {
            if (managedObject == null)
            {
                return;
            }
            if (!_extractedObjects.Contains(managedObject))
            {
                return;
            }
            (managedObject as IManagedEvent)?.OnRelease();

            _extractedObjects.Remove(managedObject);
            managedObject.Clear();
        }


        public void Release()
        {
            
        }


        public void Clear()
        {
            foreach (T obj in _extractedObjects)
            {
                (obj as IManagedEvent)?.OnRelease();
                obj.Clear();
            }
            foreach (T obj in _storedObjects)
            {
                (obj as IManagedEvent)?.OnRelease();
                obj.Clear();
            }
            _storedObjects.Clear();
            _extractedObjects.Clear();
        }

       
    }
}
